<?php
/**
 * Soar Framework - Lightweight and simple to use PHP Framework
 *
 * Soar is free software and is distributed WITHOUT ANY WARRANTY
 *
 * @package Soar
 * @version 1.0 $Id: SoarRouter.php 2 2012-04-06 21:50:33Z shay@shayanderson.com $
 * @copyright Copyright (c) 2012 ShayAnderson.com
 * @license http://www.gnu.org/licenses/gpl.html GPL License
 * @link http://www.shayanderson.com/projects/soar-php-framework.htm
 */

/**
 * Soar Framework Router Class
 *
 * @package Soar
 * @name SoarRouter
 * @author Shay Anderson 03.12
 *
 * @example
 * $router = &SoarRouter::getInstance();
 * $controller_name = $router->getControllerName();
 */
final class SoarRouter extends SoarSingleton
{
	/**
	 * Action
	 *
	 * @var string
	 */
	private $__action = 'action';

	/**
	 * Action name
	 *
	 * @var string
	 */
	private $__action_name;

	/**
	 * Config
	 *
	 * @var stdClass
	 */
	private $__conf;

	/**
	 * Controller
	 *
	 * @var string
	 */
	private $__controller = 'index';

	/**
	 * Controller name
	 *
	 * @var string
	 */
	private $__controller_name;

	/**
	 * Controller path
	 *
	 * @var string
	 */
	private $__controller_path;

	/**
	 * Module
	 *
	 * @var string
	 */
	private $__module;

	/**
	 * Module dir
	 *
	 * @var string
	 */
	private $__module_dir;

	/**
	 * Template
	 *
	 * @var string
	 */
	private $__template = 'action';

	/**
	 * Template name
	 *
	 * @var string
	 */
	private $__template_name;

	/**
	 * Template path
	 *
	 * @var string
	 */
	private $__template_path;

	/**
	 * Init route
	 */
	protected function __construct()
	{
		$this->__conf = &SoarConf::getInstance();
		$this->__setUriParts();
		Soar::log(__CLASS__ . ': initialized', SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ': Module: ' . ( $this->__module !== NULL ? '"' . $this->__module . '"' : '(none)' ),
			SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ": Module directory: \"{$this->__module_dir}\"", SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ": Controller: \"{$this->__controller_name}\"", SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ": Controller path: \"{$this->__controller_path}\"", SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ": Action: \"{$this->__action_name}\"", SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ": Template: \"{$this->__template_name}\"", SoarDebugger::LEVEL_DEBUG);
		Soar::log(__CLASS__ . ": Template path: \"{$this->getTemplatePath()}\"", SoarDebugger::LEVEL_DEBUG);
	}

	/**
	 * Format action name, ex: do-this => doThisAction
	 *
	 * @param string $action
	 * @return string
	 */
	private function __formatActionName($action)
	{
		if($action == 'action')
		{
			// do not format default action name 'action'
			return $action;
		}
		$action = str_replace(' ', NULL, ucwords(preg_replace('/[^a-zA-Z0-9]/', ' ',
			strtolower($action))))
			. $this->__conf->core->router->action_append_name;
		return strtolower(substr($action, 0, 1)) . substr($action, 1, strlen($action));
	}

	/**
	 * Format controller name, ex: index => IndexController
	 *
	 * @param string $controller
	 * @return string
	 */
	private function __formatControllerName($controller)
	{
		return str_replace(' ', NULL, ucwords(preg_replace('/[^a-zA-Z0-9]/', ' ',
			strtolower($controller))))
			. $this->__conf->core->router->controller_append_name;
	}

	/**
	 * Format template name, ex: some-action => 'controller.some-action.tpl'
	 *
	 * @param string $controller
	 * @param string $template
	 * @return string
	 */
	private function __formatTemplateName($controller, $template)
	{
		return strtolower(preg_replace('/[^a-zA-Z0-9\-]/', NULL, $controller))
			. ( $template != 'action' ?  '.' . strtolower(preg_replace('/[^a-zA-Z0-9\-]/', NULL, $template))
			: NULL );
	}

	/**
	 * Template path setter
	 *
	 * @return void
	 */
	private function __setTemplatePath()
	{
		$this->__template_path = ( $this->__module !== NULL ? $this->__module . DIRECTORY_SEPARATOR : NULL )
			. $this->__template_name . $this->__conf->core->file->ext_template;
	}

	/**
	 * Set current URI parts
	 *
	 * @return void
	 */
	private function __setUriParts()
	{
		$server = &SoarRequest::getInstance(SoarRequest::TYPE_SERVER);
		$this->__module_dir = $this->__conf->core->dir->mod;
		$this->__controller_name = $this->__formatControllerName($this->__controller);
		$this->__controller_path = $this->__module_dir . $this->__controller_name . $this->__conf->core->file->ext_php;
		$this->__action_name = $this->__formatActionName($this->__action);
		$this->__template_name = $this->__formatTemplateName($this->__controller, $this->__template);
		$this->__setTemplatePath();
		if(empty($server->REQUEST_URI) || $server->REQUEST_URI == '/')
		{
			return;
		}
		if(empty($this->__conf->core->router->uri_module_pattern)
			|| empty($this->__conf->core->router->uri_controller_pattern)
			|| empty($this->__conf->core->router->uri_action_pattern))
		{
			trigger_error('Failed to initialize router, invalid configuration for'
				. ' module, controller and/or action pattern(s)', SoarErrorHandler::ERROR);
		}
		// set module
		preg_match($this->__conf->core->router->uri_module_pattern, $server->REQUEST_URI, $matches);
		if(isset($matches[1]))
		{
			$this->__module = trim($matches[1], '/');
			// only allow directory characters
			if(preg_match('/[^\w\/\-]/', $this->__module))
			{
				trigger_error("Failed to initialize router, invalid module \"{$this->__module}\"",
					SoarErrorHandler::ERROR);
			}
			$this->__module_dir = $this->__conf->core->dir->mod . $this->__module
				. ( strlen($this->__module) > 0 ? DIRECTORY_SEPARATOR : NULL );
		}
		// set controller
		preg_match($this->__conf->core->router->uri_controller_pattern, $server->REQUEST_URI, $matches);
		if(isset($matches[1]))
		{
			$this->__controller = $matches[1];
			$this->__controller_name = $this->__formatControllerName($this->__controller);
			$this->__controller_path = $this->__module_dir . $this->__controller_name . $this->__conf->core->file->ext_php;
		}
		// set action
		preg_match($this->__conf->core->router->uri_action_pattern, $server->REQUEST_URI, $matches);
		if(isset($matches[1]))
		{
			$this->__action = $matches[1];
			$this->__action_name = $this->__formatActionName($this->__action);
		}
		// set template
		$this->__template = $this->__action;
		$this->__template_name = $this->__formatTemplateName($this->__controller, $this->__template);
		$this->__setTemplatePath();
	}

	/**
	 * Action getter
	 *
	 * @return string
	 */
	public function getAction()
	{
		return $this->__action;
	}

	/**
	 * Action name getter
	 *
	 * @return string
	 */
	public function getActionName()
	{
		return $this->__action_name;
	}

	/**
	 * Controller getter
	 *
	 * @return string
	 */
	public function getController()
	{
		return $this->__controller;
	}

	/**
	 * Controller name getter
	 *
	 * @return string
	 */
	public function getControllerName()
	{
		return $this->__controller_name;
	}

	/**
	 * Controller path getter
	 *
	 * @return string
	 */
	public function getControllerPath()
	{
		return $this->__controller_path;
	}

	/**
	 * Get self instance (singleton)
	 *
	 * @return SoarRouter
	 */
	public static function &getInstance()
	{
		parent::$_class_name = __CLASS__;
		return parent::getInstance();
	}

	/**
	 * Module getter
	 *
	 * @return string
	 */
	public function getModule()
	{
		return $this->__module;
	}

	/**
	 * Module directory getter
	 *
	 * @return string
	 */
	public function getModuleDir()
	{
		return $this->__module_dir;
	}

	/**
	 * Module parts getter
	 *
	 * @return array ('module1', 'module2', [, ...])
	 */
	public function getModuleParts()
	{
		if(strlen($this->__module) > 0)
		{
			return explode('/', $this->__module);
		}
		return array();
	}

	/**
	 * Template getter
	 *
	 * @return string
	 */
	public function getTemplate()
	{
		return $this->__template;
	}

	/**
	 * Template name getter
	 *
	 * @return string
	 */
	public function getTemplateName()
	{
		return $this->__template_name;
	}

	/**
	 * Template path getter
	 *
	 * @param bool $prepend_dir_base
	 * @return string
	 */
	public function getTemplatePath($prepend_dir_base = true)
	{
		return ( $prepend_dir_base ? $this->__conf->core->dir->tpl : NULL ) . $this->__template_path;
	}
}
?>